/**
 * 
 */
package traj.core;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.HashMap;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
import java.util.Vector;

import traj.algo.suffixtrees.NumericSuffixTree;
import traj.algo.suffixtrees.SimpleSequenceAccessor;
import traj.algo.suffixtrees.node.Node;
import traj.algo.suffixtrees.node.memory.SimpleNode;
import traj.algo.suffixtrees.node.memory.SimpleNodeAccessor;

/**
 * @author Xiaohui Li
 * 
 *         Singleton TManager It is assumed the number of trajectories is known
 *         beforehand
 */
public class TManager {
	private Vector<ITrajectory> trajectories;
	NumericSuffixTree<SimpleNode> tree;
	private static TManager t_manager = null;

	public static TManager getInstance() {
		if (t_manager == null) {
			t_manager = new TManager();
		}
		return t_manager;
	}

	public static TManager getInstance(int numTrj) {
		if (t_manager == null) {
			t_manager = new TManager(numTrj);
		}
		return t_manager;
	}

	public void addTrajectory(ITrajectory trj) {
		trajectories.add(trj);
	}

	private TManager() {
		trajectories = new Vector<ITrajectory>();
		tree = new NumericSuffixTree<SimpleNode>(new SimpleNodeAccessor(),
				new SimpleSequenceAccessor());
	}

	private TManager(int numTrj) {
		// init
		trajectories = new Vector<ITrajectory>(numTrj);
		for (int i = 0; i < numTrj; i++) {
			trajectories.add(new ITrajectory());
		}
		// init a NumericSuffixTree
		tree = new NumericSuffixTree<SimpleNode>(new SimpleNodeAccessor(),
				new SimpleSequenceAccessor());
	}

	// first read data into a big string array
	// then segment the array into vector of string vectors
	public void readData(String filename, int maxMOID, int numLines)
			throws Exception {
		BufferedReader br = new BufferedReader(new FileReader(filename));
		// ignore the title
		br.readLine();

		String line;
		int i = 0;
		HashMap<Integer, String> hm = new HashMap<Integer, String>(maxMOID);
		// String[] strArray = new String[numLines];

		while ((line = br.readLine()) != null) {
			// Check the first integer
			Integer in = new Integer(line.substring(0, 1));
			// System.out.println(in);
			if (hm.get(in) == null) {
				hm.put(in, line);
			} else {
				String content = hm.get(in);
				hm.put(in, content + "$" + line);
			}

		}
		// Util.printHashMap(hm);

		// create trajectories here
		fillTraj(hm);
	}

	private void fillTraj(HashMap<Integer, String> hm) {
		Set<Integer> keys = hm.keySet();
		Scanner sc;
		for (Integer key : keys) { // each key is one trajectory
			String str = hm.get(key);
			sc = new Scanner(str).useDelimiter("\\$");
			ITrajectory trj = new ITrajectory();
			trj.setId(key);
			while (sc.hasNext()) {
				// one line is a point
				String oneLine = sc.next();
				// System.out.println(oneLine);
				Scanner sc1 = new Scanner(oneLine);
				int oid = sc1.nextInt();
				int timestamp = sc1.nextInt();
				double x_coor = sc1.nextDouble();
				double y_coor = sc1.nextDouble();
				double speed = sc1.nextDouble();
				long rid = sc1.nextLong();
				trj.append(new TPoint(timestamp, x_coor, y_coor, speed, rid,
						Params.defaultDirection));
			}
			trajectories.add(trj);
		}

	}

	public void buildSuffixTree() {

		for (ITrajectory t : trajectories) {
			List<Long> seq = t.buildTSeqLong();
			int textNum = t.tid; // textNum is the id
			// Util.printList(seq);
			tree.addSequences(seq, textNum, false);
		}
	}

	// Find the most similar one trajectory among all trajectories
	public ITrajectory findMostSimialarTrajAll() {

		SimpleNode root = tree.getRoot();
		Vector<Node> nodes = root.getInternalChildren();
		int max_seen = 0;
		Node maxNode = null;
		for (Node node : nodes) {
			SimpleNode snode = (SimpleNode) node;
			// System.out.println(snode.getId() + ": "
			// + countWeight(snode, tree.getLabel(snode).size()));
			int val = tree.getPathLength(snode);
			if (val > max_seen) {
				max_seen = val;
				maxNode = snode;
			}
		}
		// max node has the longest similar substring

		System.out.println("Long substring start from node with id:"
				+ maxNode.getId() + "; Label:"
				+ tree.getLabel((SimpleNode) maxNode));
		System.out.println("Path Listing:");
		listPath((SimpleNode) maxNode);
		// output to a dot file
		 tree.exportDot(Params.DEST);
		return null;
	}

	private void listPath(SimpleNode root) {
		Vector<Node> nodes = root.getInternalChildren();
		if (nodes.size() == 0) {
			List<Node> children = root.getChildren();
			int i = 1;
			for (Node n : children) {
				if (i == 1) {
					System.out.print("traj_" + n.getTextNumber() + " vs ");
					i++;
				} else {
					System.out.println("traj_" + n.getTextNumber());
				}
			}
		} else {
			int max_seen = 0;
			Node nextNode = null;
			for (Node node : nodes) {
				int val = tree.getPathLength((SimpleNode) node);
				if (val > max_seen) {
					max_seen = val;
					nextNode = node;
				}
			}
			System.out.println("listing: "
					+ tree.getLabel((SimpleNode) nextNode));
		}

	}

	// TODO change return type from int to an TPair object
	private int countWeight(SimpleNode root, int weight_so_far) {
		Vector<Node> internalChildren = root.getInternalChildren();
		if (internalChildren.size() == 0) {
			return weight_so_far;
		} else {
			int max_seen = 0;
			for (Node n : internalChildren) {
				int val = countWeight((SimpleNode) n, weight_so_far
						+ tree.getLabel(root).size());
				if (val > max_seen) {
					max_seen = val;
				}
				return max_seen;
			}
		}
		return weight_so_far;

	}

	// Given query traj, find similar trajectories of which common substring
	// length is >=threshold
	public ITrajectory findMostSimialarTrajectory(ITrajectory traj,
			int threshold) {

		// TODO To implement this method
		return null;
	}

	public ITrajectory findMostSimialarTrajectory() {
		// TODO operation on the suffix tree without writing to intermediate
		// file
		return null;
	}

}
